package net.sh.dpinfrastructure.support.openapi;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import net.sh.dpinfrastructure.exception.DPException;
import net.sh.dpinfrastructure.util.DPArrayUtils;



/**
 * 
 * 基础action，所有openapi实现类需实现该类向外部提供服务
 * 
 * @version 1.0
 * @since JDK1.7
 * @author shaohuil
 * @company 上海朝阳永续信息技术有限公司
 * @copyright (c) 2015 SunTime Co'Ltd Inc. All rights reserved.
 * @date 2015年8月18日 上午11:15:34
 */
public abstract class DPBaseAction<T> {
    
    /**
     * 
     * 获取必要参数
     *
     * @return
     * 
     * @author shaohui
     * @date 2015年8月18日 上午11:16:22
     */
    public String[] getRequiredParams() {
        return new String[]{};
    };
    
    /**
     * 
     * 获取可选参数
     *
     * @return
     * 
     * @author shaohui
     * @date 2015年8月18日 上午11:17:25
     */
    public String[] getOptionalParams() {
        return new String[]{};
    }
    
    /**
     * 
     * 自定义校验
     *
     * 
     * @author shaohui
     * @date 2015年8月18日 上午11:18:21
     */
    public void customVerify(DPApiContext context) {
        
    }
    
    /**
     * 
     * action执行体
     *
     * 
     * @author shaohui
     * @date 2015年8月18日 上午11:20:27
     */
    protected abstract T execute(DPApiContext context);
    
    /**
     * 
     * 获取action资源名称，如果没有则需要去管理平台设置，仅供初始化使用<br>
     * e.g：/user/login
     *
     * @return
     * 
     * @author shaohui
     * @date 2016年4月29日 上午11:04:33
     */
    protected String getSourceName() {
        return null;
    }
    
    /**
     * 
     * 获取action版本号（仅供初始化使用）
     *
     * @return
     * 
     * @author shaohui
     * @date 2016年4月29日 上午11:06:41
     */
    protected String getVersion() {
        return "v1";
    }
    
    /**
     * 执行
     * Lancer
     * 2016年2月9日上午9:29:56
     * @param context
     * @return
     *
     */
    public final T doExecute(DPApiContext context, ExecutorService executor) {
        verifyRequest(context);
        FutureTask<T> future = new FutureTask<T>(new Callable<T>() {

            @Override
            public T call() throws Exception {
                return execute(context);
            }
        });
        if (executor == null) {
            Executors.newSingleThreadExecutor().execute(future);
        } else {
            executor.execute(future);
        }
        try {
            return future.get(getApi().getTimeout(), TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            throw new DPException("api interrupted", e);
        } catch (ExecutionException e) {
            throw new DPException("api internal error", e.getCause());
        } catch (TimeoutException e) {
            throw new DPException("api timeout", e.getCause());
        }
    }
    
    /**
     * 
     * 异步执行
     *
     * @param context
     * @return
     * 
     * @author shaohui
     * @date 2017年2月10日 下午3:38:18
     */
    public final void doExecuteAsync(DPApiContext context, final DPApiExecuteListener<T> listener) {
        verifyRequest(context);
        Executors.newSingleThreadExecutor().execute(new Runnable() {
            @Override
            public void run() {
                listener.beforeExecute(context);
                try {
                    T result = execute(context);
                    listener.onSuccess(context, result);
                } catch (Throwable t) {
                    listener.onException(context, t);
                } finally {
                    listener.afterExecute(context);
                }
            }
        });
    }
    
    /**
     * 
     * 该action关联的api（相当于元数据meta-data）
     *
     * @return
     * 
     * @author shaohui
     * @date 2017年2月10日 下午3:48:53
     */
    public abstract DPApi getApi();
    
    /**
     * 校验此次请求是否有效
     * @param context
     * 
     * @author shaohui
     * @date 2016年2月11日下午4:19:26
     *
     */
    public final void verifyRequest(DPApiContext context) {
        Map<String, String> params = null;
        // 是否完整校验
        boolean isFullVerify = context.isFullVerify();
        if (isFullVerify) { // 完整校验
//            _s = context.getParam("_s");
        }
        // 校验参数个数
        verifyParams(params);
        // 
        
        // api自定义校验
        customVerify(context);
    }
    
    /**
     * 
     * TODO:(描述). <br/> 
     * 
     * @param context
     *
     * @author shaohui
     * @date 2017年2月11日上午10:47:12
     * @since JDK 1.8
     */
    public final static void strictVerify(DPApiContext context) {
        // 检查系统参数是否存在
        checkAuthParamsExist(context);
        
        // 校验时间戳
        Long timestamp = context.getTimestamp();
        if (timestamp == null) {
            throw new DPIllegalArgumentException("empty or wrong timestamp");
        }
        if (Math.abs(System.currentTimeMillis() - timestamp) > context.getRequestTimeout()) {
            throw new DPIllegalArgumentException("request timeout");
        }
        
        // 校验app
        String appkey = context.getAppkey();
        
        
        
    }
    
    /**
     * 
     * 检查访问权限参数是否存在 <br/> 
     * 校验请求访问是否合法的第一步校验
     * 
     * @param context
     *
     * @author shaohui
     * @date 2017年2月11日下午3:00:55
     * @since JDK 1.8
     */
    public final static void checkAuthParamsExist(DPApiContext context) {
        for (String sysParam : DPApiContext.SYSTEM_PARAMS) {
            if (!context.existSysParam(sysParam)) {
                throw new DPIllegalArgumentException("empty or wrong " + sysParam + " param");
            }
        }
    }
    
    /**
     * 
     * 校验唯一请求标识
     *
     * @param sign
     * 
     * @author shaohui
     * @date 2017年2月10日 下午5:36:14
     */
    public void verifyRequestSign(String sign) {
        
    }
    
    /**
     * 
     * 校验参数个数<br>
     * 判断参数是多了还是少了
     *
     * @param params
     * 
     * @author shaohui
     * @date 2017年2月10日 下午5:27:47
     */
    public final void verifyParams(Map<String, String> params) {
        //必选参数
        String[] requiredParams = getRequiredParams(); 
        if (requiredParams == null) {
            requiredParams = new String[0];
        }
        //可选参数
        String[] optionalParams = getOptionalParams(); 
        if (optionalParams == null) {
            optionalParams = new String[0];
        }
        //实际的可选参数包括默认参数
        optionalParams = ArrayUtils.addAll(optionalParams, null);
        //实际传过来的参数（排除掉系统参数以后的参数）
        String[] requestParams = params.keySet().toArray(new String[0]);
        //缺少的参数
        Set<String> lackedParams = DPArrayUtils.getDifferdElements(requestParams, requiredParams);
        if (!CollectionUtils.isEmpty(lackedParams)) {
            throw new DPIllegalArgumentException("缺少参数：" + StringUtils.join(lackedParams, ","));
        }
        //多出来的参数
        Set<String> extraParams = DPArrayUtils.getDifferdElements(ArrayUtils.addAll(requiredParams, optionalParams), requestParams);
        if (!CollectionUtils.isEmpty(extraParams)) {
            throw new DPIllegalArgumentException("多出来的参数：" + StringUtils.join(extraParams, ","));
        }
    }
}
